﻿#define LAZY_LIB
//#define REC_LIB

using System.Collections.Generic;

namespace FinQ
{

    /// <summary>
    /// Sam's functional programming library. F'LINQ!
    /// </summary>
    public static partial class Functional
    {

#if !REC_LIB
        /// <summary>
        /// Reduce uses an initial value and an accumulation function to 
        /// build a result value from a sequence. In LINQ, Aggregate().
        /// </summary>
        /// <remarks>
        /// Base function for Map, Filter, ...
        /// Stratight-forward iterative version.
        /// </remarks>
        /// <typeparam name="T">Input type</typeparam>
        /// <typeparam name="R">Result type</typeparam>
        /// <param name="seq">Sequence</param>
        /// <param name="init">Initial value</param>
        /// <param name="agg">Aggregate function</param>
        /// <returns>Result type value</returns>
        public static R Reduce<T, R>(this IEnumerable<T> seq, R init, Agg<T, R> agg)
        {
            R result = init;

            if( null == seq ) return result;

            foreach (T item in seq)
                result = agg(item, result);

            return result;
        }
#endif


#if REC_LIB
        /// <summary>
        /// Reduce uses an initial value and an accumulation function to 
        /// build a result value from a sequence. In LINQ, Aggregate().
        /// </summary>
        /// <remarks>
        /// Recursive Evaluation Version (#define REC_LIB).
        /// Maintains extension-function interface, but swaps IEnumerable for IEnumerator
        /// and passes on to RecursiveReduce.
        /// </remarks>
        /// <typeparam name="T">Input type</typeparam>
        /// <typeparam name="R">Result type</typeparam>
        /// <param name="seq">Sequence</param>
        /// <param name="init">Initial value</param>
        /// <param name="agg">Aggregate function</param>
        /// <returns>Result type value</returns>
        public static R Reduce<T, R>(this IEnumerable<T> seq, R init, Agg<T, R> agg)
        {
            return null == seq ? init : RecursiveReduce(seq.GetEnumerator(), init, agg);
        }

        /// <summary>
        /// Recursive Reduce implemenation.
        /// </summary>
        /// <remarks>
        /// Used internally by Reduce, when REC_LIB is defined. 
        /// </remarks>
        /// <typeparam name="T">Input type</typeparam>
        /// <typeparam name="R">Result type</typeparam>
        /// <param name="it">Enumerator</param>
        /// <param name="init">Initial value</param>
        /// <param name="agg">Aggregate function</param>
        /// <returns>Result type value</returns>
        private static R RecursiveReduce<T, R>(IEnumerator<T> it, R init, Agg<T, R> agg)
        {
            return it.MoveNext() ? RecursiveReduce(it, agg(it.Current, init), agg) : init;
        }
#endif

#if LAZY_LIB
        /// <summary>
        /// Filter uses a predicate function to conditionally copy elements 
        /// from an input sequence. In LINQ, Where().
        /// </summary>
        /// <remarks>Lazy evaluation version</remarks>
        /// <typeparam name="T">Input type</typeparam>
        /// <param name="seq">Input sequence</param>
        /// <param name="pred">Predicate function</param>
        /// <returns>Result sequence</returns>
        public static IEnumerable<T> Filter<T>(this IEnumerable<T> seq, Pred<T> pred)
        {
            foreach(T it in seq)
                if (pred(it))
                    yield return it;
        }


        /// <summary>
        /// Map uses a conversion function to build a result sequence from an
        /// input sequence. In LINQ, Select()
        /// </summary>
        /// <remarks>Lazy evaluation version</remarks>
        /// <typeparam name="T">Input type</typeparam>
        /// <typeparam name="R">Result type</typeparam>
        /// <param name="seq">Input sequence</param>
        /// <param name="conv">Conversion function</param>
        /// <returns>Result sequence</returns>
        public static IEnumerable<R> Map<T, R>(this IEnumerable<T> seq, Conv<T, R> conv)
        {
            foreach( T it in seq )
                yield return conv(it);
        }
#else
        /// <summary>
        /// Filter uses a predicate function to conditionally copy elements 
        /// from an input sequence. In LINQ, Where().
        /// </summary>
        /// <typeparam name="T">Input type</typeparam>
        /// <param name="seq">Input sequence</param>
        /// <param name="pred">Predicate function</param>
        /// <returns>Result sequence</returns>
        public static IEnumerable<T> Filter<T>(this IEnumerable<T> seq, Pred<T> pred)
        {
            return seq.Reduce(new List<T>(), (item, result) =>
            {
                if (pred(item))
                    result.Add(item);
                return result;
            });
        }


        /// <summary>
        /// Map uses a conversion function to build a result sequence from an
        /// input sequence. In LINQ, Select()
        /// </summary>
        /// <typeparam name="T">Input type</typeparam>
        /// <typeparam name="R">Result type</typeparam>
        /// <param name="seq">Input sequence</param>
        /// <param name="conv">Conversion function</param>
        /// <returns>Result sequence</returns>
        public static IEnumerable<R> Map<T, R>(this IEnumerable<T> seq, Conv<T, R> conv)
        {
            return seq.Reduce(new List<R>(), (item, result) =>
            {
                result.Add(conv(item));
                return result;
            });
        }
#endif
    }
}
